﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using XDesigner.ORM.DOM;
using System.Data;
using XDesigner.Common;
using System.Collections;

namespace XDesigner.ORM
{
    public class ORMTypeMappingHelperContainer
    {

        public ORMTypeMappingHelperContainer()
        {
        }

        private ORMEngine _Engine = null;

        internal ORMEngine Engine
        {
            get { return _Engine; }
            set { _Engine = value; }
        }

        private System.Collections.Generic.Dictionary<ORMTypeInfo, ORMTypeMappingHelper>
            myMappingHelpers = new Dictionary<ORMTypeInfo, ORMTypeMappingHelper>();

        //private string GenerateHelperSourceCode(Type type)
        //{
        //    if (type == null)
        //    {
        //        throw new ArgumentNullException("type");
        //    }
        //    ORMTypeInfo ot = this.Engine.Project.Types[type];
        //    if (ot == null)
        //    {
        //        throw new NotSupportedException(type.FullName);
        //    }
        //    ORMContext context = new ORMContext(this.Engine);
        //    string code = ORMCodeGenerator.GenerateClassCode(context, ot, null);
        //    return code;
        //}

        /// <summary>
        /// Get mapping helper
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        public ORMTypeMappingHelper this[ORMTypeInfo info]
        {
            get
            {
                if (info == null)
                {
                    throw new ArgumentNullException("info");
                }
                if (info.Enabled == false)
                {
                    return null;
                }
                if (myMappingHelpers.ContainsKey(info))
                {
                    return myMappingHelpers[info];
                }
                else
                {
                    BuildHelpers();
                }
                return myMappingHelpers[info];
            }
        }

        public ORMTypeMappingHelper this[Type type]
        {
            get
            {
                if (type == null)
                {
                    throw new ArgumentNullException("type");
                }
                ORMTypeInfo ot = this.Engine.Project.Types[type];
                if (ot == null)
                {
                    throw new NotSupportedException(type.FullName);
                }
                return this[ot];
            }
        }

        public void SetHelper(Type type, ORMTypeMappingHelper helper)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            if (helper == null)
            {
                throw new ArgumentNullException("helper");
            }
            ORMTypeInfo info = this.Engine.Project.Types[type];
            if (info == null)
            {
                throw new NotSupportedException(type.FullName);
            }
            helper.ORMType = info;
            myMappingHelpers[info] = helper;
        }

        private void BuildHelpers()
        {
            // If can not find helper , then generate helper.
            ArrayList list = new ArrayList();
            foreach (ORMTypeInfo item in this.Engine.Project.Types)
            {
                if (myMappingHelpers.ContainsKey(item) == false
                    && item.Enabled)
                {
                    list.Add(item);
                    //ORMTypeMappingHelper helper = new ReflectionORMTypeMappingHelper(item);
                    //helper.Engine = this;
                    //myMappingHelpers[item] = helper;
                }
            }
            if (list.Count > 0)
            {
                XDesigner.Script.DynamicCompiler compiler = new XDesigner.Script.DynamicCompiler();
                compiler.Language = XDesigner.Script.CompilerLanguage.CSharp;
                compiler.ReferenceAssemblies.Add("System.dll");
                compiler.ReferenceAssemblies.Add("System.Data.dll");
                compiler.ReferenceAssemblies.Add("System.Xml.dll");

                List<Type> referencesType = new List<Type>();
                string strNameSpace = "NS" + Guid.NewGuid().ToString("N");

                ORMContext context = new ORMContext(this.Engine);

                compiler.SourceCode = ORMCodeGenerator.GenerateORMHelperClassesCode(
                    context,
                    (ORMTypeInfo[])list.ToArray(typeof(ORMTypeInfo)),
                    strNameSpace,
                    referencesType);
                referencesType.Add(this.Engine.GetType());
                referencesType.Add(typeof(ORMEngine));
                foreach (Type type in referencesType)
                {
                    string location = type.Assembly.Location;
                    if (compiler.ReferenceAssemblies.Contains(location) == false)
                    {
                        compiler.ReferenceAssemblies.Add(location);
                    }
                }

                if (compiler.Compile())
                {
                    System.Reflection.Assembly asm = compiler.ResultAssembly;
                    foreach (Type t in asm.GetTypes())
                    {
                        if (t.BaseType.Equals(typeof(ORMTypeMappingHelper)))
                        {
                            ORMTypeMappingHelper helper = (ORMTypeMappingHelper)System.Activator.CreateInstance(t);
                            helper.Engine = this.Engine;
                            foreach (ORMTypeInfo ot in list)
                            {
                                if (helper.InstanceType.Equals(ot.InstanceType)
                                    || helper.InstanceType.IsSubclassOf(ot.InstanceType))
                                {
                                    helper.ORMType = ot;
                                    myMappingHelpers[ot] = helper;
                                    break;
                                }//if
                            }//foreach
                        }//if

                        //if (t.Namespace == strNameSpace)
                        //{
                        //    foreach (ORMTypeInfo ot in list)
                        //    {
                        //        string typeName = ORMCodeGenerator.ToTypeNameCode(ot.InstanceType, false)
                        //            + "ORMTypeMappingHelper";

                        //        if (t.Name == typeName)
                        //        {
                        //            ORMTypeMappingHelper helper = (ORMTypeMappingHelper)
                        //                System.Activator.CreateInstance(t);
                        //            helper.Engine = this.Engine;
                        //            helper.ORMType = ot;
                        //            myMappingHelpers[ot] = helper;
                        //            break;
                        //        }//if
                        //    }//foreach
                        //}//if
                    }//foreach
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine(
                        compiler.SourceCodeWithCompilerErrorMessage);
                    string msg = string.Format(
                        ORMStrings.DynamicCompilerError_Message,
                        compiler.CompilerErrorMessage);
                    if (this.Engine.Config.ThrowDynamicCompilerError)
                    {
                        throw new Exception(msg);
                    }
                }
            }//if
        }

    }
}
